//
//  TableViewImageViewOptimizationViewController.swift
//  iOS-RxSwift-Tutorials_Example
//
//  Created by mengru.tian on 2020/9/24.
//  Copyright © 2020 CocoaPods. All rights reserved.
//

import UIKit
import RxSwift

class TableViewImageViewOptimizationViewController: UIViewController {
    
    @IBOutlet weak var tableView: UITableView!
    
    let disposeBag = DisposeBag()
    
    //电影条目数据
    var movies = [MovieRecord]()
    
    override func viewDidLoad() {
        super.viewDidLoad()

        title = "58.表格图片加载优化"
        view.backgroundColor = .white
        
        test()
    }
    
    func test() {
        //创建一个重用的单元格
        self.tableView!.register(MyTableCell.self, forCellReuseIdentifier: "Cell")
        //单元格无法选中
        self.tableView.allowsSelection = false
        //设置单元格高度
        self.tableView!.rowHeight = 100
         //数据源地址
       let dataSourcePath = Bundle.main.path(forResource: "movies", ofType: "plist")
       let datasourceDictionary = NSDictionary(contentsOfFile: dataSourcePath!)
         
        //初始化数据
        for(key,value) in datasourceDictionary!{
            let name = key as? String
            let url = URL(string:value as? String ?? "")
            if name != nil && url != nil {
                let movieRecord = MovieRecord(name:name!, url:url!)
                self.movies.append(movieRecord)
            }
        }
         
        //设置单元格数据（其实就是对 cellForRowAt 的封装）
        Observable.just(movies)
            .bind(to: tableView.rx.items) { (tableView, row, element) in
                //初始化cell
                let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
                    as! MyTableCell
                //设置单元格数据
                cell.movieRecord = element
                //返回单元格
                return cell
            }
            .disposed(by: disposeBag)
    }

}

extension TableViewImageViewOptimizationViewController {
    // 这个枚举包含所有电影图片的状态
    enum MovieRecordState {
        case new, downloaded, filtered, failed
    }
     
    // 电影条目类
    class MovieRecord {
        let name:String //电影标题
        let url:URL //电影海报图片地址
        var image:UIImage? //电影海报图片
         
        var state = MovieRecordState.new //图片当前状态
         
        init(name:String, url:URL) {
            self.name = name
            self.url = url
        }
    }
    //单元格类
    class MyTableCell: UITableViewCell {
         
        var disposeBag: DisposeBag?
         
        //单元格尾部的活动指示器
        var indicator:UIActivityIndicatorView!
         
        //当前显示的数据
        var movieRecord:MovieRecord! {
            didSet{
                let disposeBag = DisposeBag() //保证 cell 被重用的时候不会被多次订阅
                 
                //设置标题
                self.textLabel?.text = movieRecord.name
                 
                //先判断图片当前的状态
                if movieRecord.state == .filtered || movieRecord.state == .failed {
                    //之前已经处理完毕的就直接显示出来
                    self.imageView?.image = movieRecord.image
                }else{
                    //只要图片状态是没出处理完毕的，一律先显示个占位符图片
                    self.imageView?.image = UIImage(named: "loading_placeholder")
                    //显示活动指示器
                    self.indicator.startAnimating()
                    //订阅序列
                    Observable.of(movieRecord)
                        .delay(0.3, scheduler: MainScheduler.instance) //延迟0.3秒
                        .observeOn(ConcurrentDispatchQueueScheduler(qos: .userInitiated))//后台
                        .flatMap { return self.fetchImage($0) }  //下载电影图片
                        .flatMap { self.applySepiaFilter($0) } //给图片添加滤镜
                        .observeOn(MainScheduler.instance)  //回到主线程显示图片
                        .subscribe(onNext: {
                            self.imageView?.image = $0.image
                            self.indicator.stopAnimating()
                        })
                        .disposed(by: disposeBag) //cell离开可视区域后自动取消订阅
                }
                 
                self.disposeBag = disposeBag
            }
        }
         
        //单元格重用时调用
        override func prepareForReuse() {
            super.prepareForReuse()
             
            disposeBag = nil //保证 cell 被重用的时候不会被多次订阅
        }
         
        //初始化
        override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
            super.init(style: style, reuseIdentifier: reuseIdentifier)
             
            //为了提示用户，将cell的accessory view设置为UIActivityIndicatorView。
            indicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
            self.accessoryView = indicator
        }
         
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
         
        //获取图片
        func fetchImage(_ movie:MovieRecord) -> Observable<MovieRecord> {
            //如果当前的电影条目还未获取图片则去获取图片
            if movie.state == .new {
                if let imageData = try? Data(contentsOf: movie.url){
                    movie.image = UIImage(data:imageData)
                    movie.state = .downloaded  //图片状态改成下载成功
                }else{
                    movie.image = UIImage(named: "fail_placeholder")! //未加载到海报显示默认的“暂无图片”
                    movie.state = .failed //图片状态改成失败
                }
            }
             
            return Observable.of(movie)
        }
         
        //给图片添加棕褐色滤镜
        func applySepiaFilter(_ movie:MovieRecord) -> Observable<MovieRecord> {
            //只有刚下载的图片需要处理
            if movie.state == .downloaded {
                let inputImage = CIImage(data:UIImagePNGRepresentation(movie.image!)!)
                let context = CIContext(options:nil)
                let filter = CIFilter(name:"CISepiaTone")
                filter!.setValue(inputImage, forKey: kCIInputImageKey)
                filter!.setValue(0.8, forKey: "inputIntensity")
                if let outputImage = filter!.outputImage {
                    let outImage = context.createCGImage(outputImage, from: outputImage.extent)
                    movie.image = UIImage(cgImage: outImage!)
                    movie.state = .filtered //图片状态改成添加滤镜成功
                } else {
                    movie.image = UIImage(named: "fail_placeholder")! //处理失败的海报显示默认的“暂无图片”
                    movie.state = .failed //图片状态改成失败
                }
            }
            return Observable.of(movie)
        }
    }
}
